package gs

import (
	"encoding/json"
	"fmt"
	"sync"
)

var (
	lock = sync.RWMutex{}
)

type Dict[T any] map[string]T
type Map[K comparable, T any] map[K]T

func AsDict[T any](c any) (d Dict[T]) {
	d = make(Dict[T])
	for k, v := range c.(map[string]any) {
		d[k] = v.(T)
	}
	return
}

func (d Dict[T]) Json() Str {
	buf, err := json.Marshal(d)
	if err != nil {
		panic(err)
	}
	return Str(buf)
}

func (d Dict[T]) Len() int {
	return len(d)
}

func (d Dict[T]) Every(e func(k string, v T)) Dict[T] {
	for k, v := range d {
		e(k, v)
	}
	return d
}

func (d Dict[T]) Filter(e func(k string, v T) bool) Dict[T] {
	e2 := make(Dict[T])
	for k, v := range d {
		if e(k, v) {
			e2[k] = v
		}
	}
	return d
}

func (d Dict[T]) Keys() (keys List[Str]) {
	for k := range d {
		keys = append(keys, Str(k))
	}
	return
}

func (d Dict[T]) Reverse() (e2 Dict[Str]) {
	e2 = make(Dict[Str])
	for k, v := range d {
		e2[fmt.Sprint(v)] = Str(k)
	}
	return
}

func (d Dict[T]) Update(o Dict[T]) Dict[T] {
	for k, v := range o {
		d[k] = v
	}
	return d
}

func (d Dict[T]) AsURI() Str {
	return d.Format("=", "&")
}

/*
Format

	eq =
	sep &

will format to uri format

	eq =
	sep ;

will format to cookie format
*/
func (d Dict[T]) Format(eq string, sep string) Str {
	e := Str("")
	for k, v := range d {
		if e != "" {
			e += Str(sep)
		}
		e += Str(k + eq + S(v).Str())
	}
	return e
}

func (d Dict[T]) FilterSelf(ifKeep func(k string, v T) bool) Dict[T] {
	removedKeys := List[string]{}

	d.Every(func(k string, v T) {
		if !ifKeep(k, v) {
			removedKeys = removedKeys.Add(k)
		}
	})

	lock.Lock()
	removedKeys.Every(func(_ int, i string) {
		delete(d, i)
	})
	lock.Unlock()
	return d
}

func (d Dict[T]) With(changeFunc func(k string, v T) T) Dict[T] {
	_tmpD := make(Dict[T])
	d.Every(func(k string, v T) {
		_tmpD[k] = changeFunc(k, v)
	})
	return _tmpD
}

func (d Dict[T]) Map() map[string]T {
	e := make(map[string]T)
	d.Every(func(k string, v T) {
		e[k] = v
	})
	return e
}

func (d Dict[T]) MapString() map[string]string {
	e := make(map[string]string)
	d.Every(func(k string, v T) {
		e[k] = fmt.Sprint(v)
	})
	return e
}
